Skip to content

마이페이지 기능 구현 및 디자인 개선#690

Merged
HA-SEUNG-JEONG merged 23 commits into
developfrom
fix/myPage
May 25, 2026
Merged

마이페이지 기능 구현 및 디자인 개선#690
HA-SEUNG-JEONG merged 23 commits into
developfrom
fix/myPage

Conversation

@HA-SEUNG-JEONG

@HA-SEUNG-JEONG HA-SEUNG-JEONG commented May 24, 2026

Copy link
Copy Markdown
Contributor

Problem

마이페이지 관련 기능이 미구현 상태이거나 디자인 토큰 불일치 및 UI 완성도 부족 문제가 있었습니다.

  • 내가 작성한 글 / 1:1 문의 페이지 미구현
  • 마이클래스 알림 토글이 비활성화 불가 (설정만 가능)
  • 탈퇴 모달 폰트 크기·색상 토큰이 Figma 디자인과 불일치
  • 전역 primary-500 등 미존재 토큰 사용

Solution

  • 내가 작성한 글·1:1 문의 페이지 신규 구현 및 API 훅(TanStack Query) + Zod 스키마 추가
  • 마이클래스 알림 토글에 비활성화 모달 추가, localStorage 기반 클라이언트 상태 관리로 즉시 반영
  • 커스텀 Toggle 컴포넌트를 공통 ToggleSwitch로 교체
  • 탈퇴 모달 폰트 (font-designer-18r/24b), 색상(text-text-default), 간격 토큰 Figma 기준으로 수정
  • 미존재 primary-500text-text-brand, bg-fill-brand-default-default 등 시스템 토큰으로 전환

Changes

Features

File Description
src/app/(service)/(my)/my-posts/page.tsx 내가 작성한 글 페이지 구현
src/app/(service)/(my)/my-inquiry/page.tsx 1:1 문의 목록 페이지 구현
src/app/(service)/(my)/my-inquiry/write/page.tsx 1:1 문의 작성 페이지 구현
src/hooks/queries/my-inquiry/inquiry-api.ts 1:1 문의 API 훅 추가
src/types/schemas/inquiry.schema.ts 문의 Zod 스키마 추가
src/api/client/axios.ts v6 전용 axiosInstanceV6 추가
src/app/(service)/(my)/my-class/_components/disable-notification-modal.tsx 알림 비활성화 확인 모달 추가

Bug Fixes

File Description
src/app/(service)/(my)/my-page/page.tsx 배너 UI 업데이트 및 토큰 수정
src/app/(service)/(my)/my-class/page.tsx 알림 토글 비활성화 기능 추가, 토큰 정리, GiftEmailCard 제거
src/app/(service)/(my)/my-page/_components/withdrawal-confirm-modal.tsx Figma 디자인 반영 (폰트·색상·간격 토큰)

Result

  • 내가 작성한 글·1:1 문의 페이지에서 API 데이터 조회 및 문의 작성 가능
  • 마이클래스 알림 토글 ON/OFF 양방향 제어 가능
  • 탈퇴 모달 텍스트가 Figma 기준 18px/24px 폰트, gray-800 색상으로 표시

Screenshots

Before After
탈퇴 모달 14px 본문 탈퇴 모달 18px 본문, 제목 24px

Test plan

  • 마이페이지 > 내가 작성한 글 탭 진입 시 게시물 목록 정상 조회
  • 마이페이지 > 1:1 문의 탭 진입 시 문의 목록 정상 조회
  • 1:1 문의 작성 후 목록에 반영되는지 확인
  • 마이클래스 알림 토글 ON → OFF 클릭 시 비활성화 모달 노출 및 해제 확인
  • 마이클래스 알림 토글 OFF → ON 클릭 시 알림 설정 모달 정상 진입
  • 탈퇴 모달 폰트 크기·색상 Figma와 일치 확인
  • 전체 페이지 yarn typecheck 통과 확인

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • 수업 환불 요청·결제 취소 흐름 및 모달 추가
    • 알림(학습 알림/알림톡) 토글·시간 저장·끄기 확인 모달 개선
    • 1:1 문의 작성(카테고리 기반)/임시저장/상세 조회 훅 및 폼 검증 추가
    • 빌더 피드 관리(필터, 임시저장, 삭제, 관리용 조회) 및 결제 상세 조회 훅 추가
  • Style

    • 마이페이지·사이드바·버튼·카드 등 브랜드 색상·스타일 일괄 조정
    • 프로필 배너 레이아웃 및 링크/아이콘 업데이트
  • Chores

    • v6 API 클라이언트와 관련 타입·훅 도입, 더미 데이터 제거 및 실제 API 연동 준비

Review Change Stack

HA-SEUNG-JEONG and others added 10 commits May 24, 2026 01:19
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
백엔드 응답에 없는 saved 필드 제거

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
primary-500 미존재 토큰을 fill-brand-default-default 등으로 교체,
GiftEmailCard 제거, disabled 버튼 스타일 수정

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
디스코드/피드 배너 레이아웃 개선, primary-500 미존재 토큰을
text-text-brand/border-border-brand으로 교체, 사이드바 라벨 변경

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
bg/text/border-primary-500, bg-brand-primary-500을 프로젝트 토큰
(fill-brand-default-default, text-brand, border-brand, rose-500 등)으로 일괄 교체

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
discord-icon.png, feed-icon.svg 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vercel

vercel Bot commented May 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
study-platform-client-dev Ready Ready Preview, Comment May 25, 2026 12:23am

@coderabbitai

coderabbitai Bot commented May 24, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@HA-SEUNG-JEONG, we couldn't start this review because you've used your available PR reviews for now.

Your plan includes 1 review of capacity. Refill in 11 minutes and 28 seconds.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more review capacity refills, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a617aeb5-0dde-4b33-be61-a44adbbf34c8

📥 Commits

Reviewing files that changed from the base of the PR and between 0db37fc and 06afccf.

📒 Files selected for processing (3)
  • .github/workflows/ci.yml
  • e2e/class/builder-feed.spec.ts
  • src/app/(landing)/class/[slug]/(learning)/feed/write/page.tsx
📝 Walkthrough

Walkthrough

알림 모달·로컬 비활성화 도입, 1:1 문의 훅·스키마 추가, 마이페이지 프로필·네비 및 모달 스타일 변경, 빌더 피드 필터·드래프트·삭제 기능, 결제 환불/가상계좌 흐름과 axios v6 클라이언트 추가를 통합합니다.

Changes

학습 알림 설정 및 모달 기능 확장

Layer / File(s) Summary
모달 컴포넌트 및 콜백
src/app/(service)/(my)/my-class/_components/disable-notification-modal.tsx, src/app/(service)/(my)/my-class/_components/learning-notification-modal.tsx
DisableNotificationModal 추가, LearningNotificationModalonSuccess?: () => void prop 추가 및 성공 후 호출.
MyClassPage 로컬 상태·토글 통합
src/app/(service)/(my)/my-class/page.tsx
localStorage 기반 locallyDisabled 도입, isEnabled 계산 통합, ToggleSwitch 교체, 모달 열림/핸들러 연결 및 AlarmCard 스타일/버튼 disabled 제어.
AlarmCard 스타일·NotificationSetting 타입
src/hooks/queries/notification/use-notification-setting.ts, src/app/(service)/(my)/my-class/page.tsx
AlarmCard의 경계/텍스트/구분선/버튼 스타일 브랜드 계열로 변경 및 NotificationSettingResponse에 notifyHour/notifyMinute/isEnabled 필드 반영.

1:1 문의 시스템 구현

Layer / File(s) Summary
API 훅 및 폼 스키마 정의
src/hooks/queries/my-inquiry/inquiry-api.ts, src/types/schemas/inquiry.schema.ts
INQUIRY_CATEGORIES·INQUIRY_CATEGORY_LABELS·inquirySchema 추가 및 useGetMyOneToOneInquiries/useGetMyOneToOneInquiryDetail/useCreateMyOneToOneInquiry/useSaveDraftOneToOneInquiry 훅 구현.
MyInquiry 목록/작성 페이지 연결
src/app/(service)/(my)/my-inquiry/page.tsx, src/app/(service)/(my)/my-inquiry/write/page.tsx
더미 제거·목록/상세 훅 연결, 상태 렌더링 재구성, 작성 폼을 카테고리 기반으로 변경하고 임시저장·제출을 백엔드 훅으로 처리.

마이페이지 프로필 및 네비게이션 개선

Layer / File(s) Summary
프로필 편집 UI 및 배너 개선
src/app/(service)/(my)/my-page/page.tsx
ProfileRow 라벨/버튼 스타일 변경, 닉네임/자기소개 버튼 브랜드 스타일 적용, 배너 카드(Discord/빌더 피드) 레이아웃 및 링크 수정.
사이드바·모바일 네비·모달 스타일
src/components/common/layout/sidebar/my-page-sidebar.tsx, src/components/common/layout/sidebar/my-page-mobile-nav.tsx, src/app/(service)/(my)/my-page/_components/withdrawal-confirm-modal.tsx, src/components/common/modals/user-profile-modal.tsx
사이드바 라벨을 '빌더 프로필'로 변경, 활성 아이콘/보더 색상 브랜드 계열로 통일, WithdrawalConfirmModal variant→className 전환, UserProfileModal 배지/버튼 배경 클래스 변경.

빌더 피드 관리 및 드래프트

Layer / File(s) Summary
MyPosts 통계·필터·목록
src/app/(service)/(my)/my-posts/page.tsx
통계 레이블/값 변경, selectedCourseId/selectedLessonId 필터 상태 추가, PUBLISHED/DRAFT 쿼리 분기 및 탭 상태 처리.
FeedCard 메뉴 및 드래프트 섹션
src/app/(service)/(my)/my-posts/page.tsx, src/app/(landing)/class/[slug]/(learning)/feed/write/page.tsx
FilterDropdown(바깥 클릭 닫기), FeedCard/DraftFeedCard 더보기 메뉴·삭제 변이·토스트 연동, FeedWrite/QnAWrite에 로컬 드래프트 저장·복원 및 임시저장 버튼 도입.

결제·환불 흐름 및 API v6 도입

Layer / File(s) Summary
결제 취소·환불 모달 및 통합
src/components/payment/modals/class-cancel-payment-modal.tsx, src/components/payment/modals/class-refund-request-modal.tsx, src/app/(service)/(my)/class-payment-management/page.tsx
ClassCancelPaymentModal의 입력 간소화, ClassRefundRequestModal 신규 추가(사유 드롭다운·상세), ClassPaymentManagementPage에서 refund modal·VA 상세 조회와 PaymentCard 버튼/상태 연결.
course API 훅·타입 확장 (v6)
src/hooks/queries/course/course-api.ts, src/types/api/course.types.ts
axiosInstanceV6 사용으로 useGetMyCoursePaymentDetail, useRequestCourseRefund, useGetMyBuilderFeedManagement 훅 추가 및 CoursePaymentDetail/Refund 타입·MyBuilderFeedManagementResponse 등 타입 추가.
API v6 axios 클라이언트
src/api/client/axios.ts
axiosInstanceV6 추가: /api/v6/ 베이스, client-v6-json 로거, 액세스 토큰 인터셉터 및 auth 재요청 핸들러 연결.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 토끼가 인사해요, 모달 열고 닫는 춤을,
문의는 잘 보내요, 드래프트는 살려두고,
피드와 배너는 반짝, 버튼 색은 브랜드로,
결제는 v6에 맡기고 환불창은 살짝 띄워요,
로컬에 끄고 켜고 저장하며 오늘도 코드가 폴짝.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.63% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 마이페이지 기능 구현과 디자인 개선이라는 PR의 주요 변경사항을 명확하게 요약하고 있습니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/myPage

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/(service)/(my)/my-inquiry/write/page.tsx (1)

50-53: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

첨부 이미지가 실제 요청에는 한 번도 포함되지 않습니다.

사용자가 파일을 선택해도 로컬 상태에만 쌓이고, 임시저장 요청에는 첨부 정보가 없으며 등록 요청은 항상 inquiryAttachmentKeys: []를 보냅니다. 지금 UI는 첨부가 저장된 것처럼 보이는데 실제로는 전부 버려집니다. 업로드/키 발급 플로우를 연결하기 전이라면 첨부 UI를 숨기거나 비활성화하는 편이 안전합니다.

Also applies to: 62-68, 86-92

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/`(service)/(my)/my-inquiry/write/page.tsx around lines 50 - 53, The
image input currently only updates local state via handleImageChange and
setImages and never uploads files or attaches generated keys to requests, so
temp-save/submit always send inquiryAttachmentKeys: []; either wire the
upload/key-issuance flow so selected files are uploaded and their attachment
keys are stored and included in the payloads used by the tempSave and submit
handlers, or disable/hide the attachment UI until that flow exists; specifically
update handleImageChange and the images state to call your upload function that
returns keys (or map files→keys), store those keys (e.g., attachmentKeys state),
and ensure the temp-save/submit routines reference attachmentKeys instead of
always sending an empty array (or conditionally render/disable the file input
element until implemented).
🧹 Nitpick comments (1)
src/app/(service)/(my)/my-posts/page.tsx (1)

249-256: ⚡ Quick win

API 기반 이미지 URL은 <Image> 렌더 전에 허용 도메인 검증이 필요합니다.

feed.thumbnailUrl을 바로 <Image src>에 넣고 있어, next.config 허용 도메인 밖 URL이면 런타임에서 깨질 수 있습니다. 공통 URL 가드 유틸로 선검증 후, 실패 시 플레이스홀더만 렌더링하는 방식으로 바꿔 주세요.

Based on learnings API-sourced URLs in this repository should be validated against next.config.ts remotePatterns before rendering <Image>, instead of relying on onError.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/`(service)/(my)/my-posts/page.tsx around lines 249 - 256, Validate
feed.thumbnailUrl against the app's allowed remote image patterns before passing
it to the Next.js <Image> component: add or use a common URL guard (e.g.,
isAllowedRemoteImage or validateRemoteImageUrl) to check feed.thumbnailUrl; only
render <Image src={feed.thumbnailUrl} ...> when the guard returns true and
imgError is false, otherwise render the placeholder fallback; keep the onError
handler to flip setImgError(true) but do not rely on it as the primary
validation and ensure the guard uses the same remotePatterns defined in
next.config (or a central export) so allowed domains stay in sync.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app/`(service)/(my)/class-payment-management/page.tsx:
- Line 239: Replace the hardcoded Tailwind color utilities "from-rose-500
to-rose-300" used in the div with className "h-1000 w-1000 flex-shrink-0
rounded-150 bg-gradient-to-br from-rose-500 to-rose-300" by the project's theme
gradient token defined in global.css (e.g., a bg-fill-brand-* or the appropriate
gradient token); edit that className to remove the literal color utilities and
apply the single theme token class so the component relies on `@theme` inline
tokens rather than hardcoded Tailwind colors.

In `@src/app/`(service)/(my)/my-class/page.tsx:
- Line 204: The conditional divider uses a raw Tailwind color ('bg-rose-200')
which violates the project's styling tokens rule; in the expression
className={cn('h-px', isEnabled ? 'bg-rose-200' : 'bg-border-default')}, replace
'bg-rose-200' with the appropriate project custom token class defined in
global.css (the same token used elsewhere for active/primary dividers in
src/app, e.g., the project's "active divider" or "accent" token) so both
branches use project tokens instead of raw Tailwind colors.
- Around line 42-46: The component reads localStorage during initial render
which causes SSR/CSR mismatch; change the useState initializer for
locallyDisabled to a constant default (e.g., false) and add a useEffect that, on
mount, reads localStorage.getItem(NOTIFICATION_DISABLE_KEY) and calls
setLocallyDisabled accordingly (use the existing locallyDisabled and
setLocallyDisabled identifiers). Also replace hardcoded Tailwind color classes
(e.g., bg-rose-200, bg-gray-200) used in this file with semantic theme tokens
defined in global.css (create/consume classes like bg-accent-weak or bg-muted
that map to your CSS variables) so the component uses the theme tokens instead
of direct color utilities.

In `@src/app/`(service)/(my)/my-inquiry/page.tsx:
- Around line 39-40: The page currently treats a failed fetch as empty because
it only reads `inquiries` from useGetMyOneToOneInquiries(); update the component
to detect and handle the query error instead of rendering the “no inquiries”
empty state: use the error information returned by the hook (or catch the
rejection) and pass it to the centralized error handler in
utils/error-handler.ts (which will use useToastStore) rather than calling alert
or silently rendering an empty list; ensure both the initial query
(useGetMyOneToOneInquiries) and the related code at the other occurrence (lines
~69-73) call the same handler and render an error UI or loading state until data
is valid.
- Around line 95-96: The detail query call using useGetMyOneToOneInquiryDetail
(with expanded ? inquiry.oneToOneInquiryId : null) currently only handles
loading/success and leaves the panel empty on failure; update the component to
read the hook's error state (e.g., isError and error) alongside detail and
detailLoading, call the centralized error handler from utils/error-handler.ts
(and/or useToastStore) to report the error, and render an explicit
error/fallback UI in the panel (a brief error message or retry button) when
isError is true so the user doesn’t see an empty panel; apply the same pattern
to the related rendering block around the 139-173 area.

In `@src/app/`(service)/(my)/my-posts/page.tsx:
- Line 248: Replace the forbidden Tailwind arbitrary class "aspect-[3/2]" on the
<div className="relative aspect-[3/2] w-full bg-gray-100"> JSX element in
page.tsx with the project’s predefined aspect-ratio token/utility (e.g., the
project's aspect token such as aspect-<token> or ratio-<token>); locate that JSX
node (in the default export component of page.tsx) and swap the arbitrary value
for the correct token/utility class used across the codebase.
- Around line 110-131: The three filter buttons in my-posts page.tsx (the
buttons with visible labels "코스", "레슨", and "최신순") are purely presentational and
need either real handlers or a disabled visual state; fix by wiring them to
state and handlers (e.g., add a useState like selectedFilter/setSelectedFilter
and a handleFilterClick that toggles "코스" vs "레슨", and a
selectedSort/setSelectedSort + handleSortClick for "최신순", then pass
onClick={handleFilterClick("코스")} etc. and update rendering based on selected
values), or if the feature isn’t ready, add disabled and aria-disabled
attributes and apply a dimmed class (e.g., opacity-50, cursor-not-allowed) to
each button so they are visually and semantically non-interactive; ensure
accessibility attributes (aria-pressed or aria-disabled) reflect the chosen
approach.
- Around line 78-79: Replace hardcoded Tailwind color classes in the conditional
className (the ternary that returns 'border-b-2 border-rose-500 text-rose-500'
vs 'font-designer-20r text-gray-800') and the other occurrences mentioned (the
lines using 'border-rose-300' and 'hover:text-red-500') with the project’s
design token classes defined in global.css; locate the conditional that sets
those class strings in src/app/(service)/(my)/my-posts/page.tsx and swap each
color class (border-rose-500, text-rose-500, border-rose-300,
hover:text-red-500, text-gray-800 as appropriate) for the equivalent token-based
classes (the token names used elsewhere in global.css) so styling follows the
token convention.

In `@src/types/schemas/inquiry.schema.ts`:
- Around line 26-29: The inquiryContent string schema currently uses .min(1)
which allows strings containing only whitespace; update the schema for
inquiryContent to reject whitespace-only input by trimming or validating
whitespace before length check—for example, apply .transform(s => s.trim()) then
.min(1, '내용을 입력해 주세요.') or use .refine(s => s.trim().length > 0, { message: '내용을
입력해 주세요.' }) so the schema behavior matches the form's trim logic.

---

Outside diff comments:
In `@src/app/`(service)/(my)/my-inquiry/write/page.tsx:
- Around line 50-53: The image input currently only updates local state via
handleImageChange and setImages and never uploads files or attaches generated
keys to requests, so temp-save/submit always send inquiryAttachmentKeys: [];
either wire the upload/key-issuance flow so selected files are uploaded and
their attachment keys are stored and included in the payloads used by the
tempSave and submit handlers, or disable/hide the attachment UI until that flow
exists; specifically update handleImageChange and the images state to call your
upload function that returns keys (or map files→keys), store those keys (e.g.,
attachmentKeys state), and ensure the temp-save/submit routines reference
attachmentKeys instead of always sending an empty array (or conditionally
render/disable the file input element until implemented).

---

Nitpick comments:
In `@src/app/`(service)/(my)/my-posts/page.tsx:
- Around line 249-256: Validate feed.thumbnailUrl against the app's allowed
remote image patterns before passing it to the Next.js <Image> component: add or
use a common URL guard (e.g., isAllowedRemoteImage or validateRemoteImageUrl) to
check feed.thumbnailUrl; only render <Image src={feed.thumbnailUrl} ...> when
the guard returns true and imgError is false, otherwise render the placeholder
fallback; keep the onError handler to flip setImgError(true) but do not rely on
it as the primary validation and ensure the guard uses the same remotePatterns
defined in next.config (or a central export) so allowed domains stay in sync.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dfa985e9-5ce0-4f98-9d57-46f3813e886f

📥 Commits

Reviewing files that changed from the base of the PR and between 25fb47e and b0b76f5.

⛔ Files ignored due to path filters (2)
  • public/my-page/discord-icon.png is excluded by !**/*.png
  • public/my-page/feed-icon.svg is excluded by !**/*.svg
📒 Files selected for processing (17)
  • src/api/client/axios.ts
  • src/app/(service)/(my)/class-payment-management/page.tsx
  • src/app/(service)/(my)/my-class/_components/disable-notification-modal.tsx
  • src/app/(service)/(my)/my-class/_components/learning-notification-modal.tsx
  • src/app/(service)/(my)/my-class/page.tsx
  • src/app/(service)/(my)/my-inquiry/page.tsx
  • src/app/(service)/(my)/my-inquiry/write/page.tsx
  • src/app/(service)/(my)/my-page/_components/withdrawal-confirm-modal.tsx
  • src/app/(service)/(my)/my-page/page.tsx
  • src/app/(service)/(my)/my-posts/page.tsx
  • src/app/(service)/(my)/payment-management/page.tsx
  • src/components/common/layout/sidebar/my-page-mobile-nav.tsx
  • src/components/common/layout/sidebar/my-page-sidebar.tsx
  • src/components/common/modals/user-profile-modal.tsx
  • src/hooks/queries/my-inquiry/inquiry-api.ts
  • src/hooks/queries/notification/use-notification-setting.ts
  • src/types/schemas/inquiry.schema.ts
💤 Files with no reviewable changes (1)
  • src/hooks/queries/notification/use-notification-setting.ts

<div className="border-border-subtle rounded-200 flex gap-300 border p-300">
{/* 썸네일 */}
<div className="h-1000 w-1000 flex-shrink-0 rounded-150 bg-gradient-to-br from-primary-500 to-rose-300" />
<div className="h-1000 w-1000 flex-shrink-0 rounded-150 bg-gradient-to-br from-rose-500 to-rose-300" />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

그라데이션 색상에 하드코딩된 Tailwind 유틸리티 사용

from-rose-500 to-rose-300은 표준 Tailwind 색상 유틸리티이며, 프로젝트의 global.css에 정의된 커스텀 토큰이 아닙니다. 코딩 가이드라인에 따르면, src/app/**/*.{tsx,jsx} 파일에서는 하드코딩된 색상을 사용하지 않고 global.css@theme inline 토큰만 사용해야 합니다.

bg-fill-brand-* 또는 프로젝트에 정의된 적절한 그라데이션 토큰으로 교체하시기 바랍니다.

As per coding guidelines: src/app/**/*.{tsx,jsx}: No hardcoded colors or spacing values. Use only @theme inline tokens from global.css.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/`(service)/(my)/class-payment-management/page.tsx at line 239,
Replace the hardcoded Tailwind color utilities "from-rose-500 to-rose-300" used
in the div with className "h-1000 w-1000 flex-shrink-0 rounded-150
bg-gradient-to-br from-rose-500 to-rose-300" by the project's theme gradient
token defined in global.css (e.g., a bg-fill-brand-* or the appropriate gradient
token); edit that className to remove the literal color utilities and apply the
single theme token class so the component relies on `@theme` inline tokens rather
than hardcoded Tailwind colors.

Comment thread src/app/(service)/(my)/my-class/page.tsx Outdated
Comment thread src/app/(service)/(my)/my-class/page.tsx
Comment on lines 39 to 40
const { data: inquiries, isLoading } = useGetMyOneToOneInquiries();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

목록 조회 실패를 빈 상태로 숨기지 마세요.

지금은 목록 쿼리가 실패해도 inquiriesundefined라서 곧바로 “아직 작성한 문의가 없어요”가 렌더링됩니다. 서버 오류를 빈 데이터로 오인하게 됩니다.

🔧 제안
-  const { data: inquiries, isLoading } = useGetMyOneToOneInquiries();
+  const {
+    data: inquiries,
+    isLoading,
+    isError,
+  } = useGetMyOneToOneInquiries();
...
-      ) : !inquiries || inquiries.length === 0 ? (
+      ) : isError ? (
+        <div className="flex items-center justify-center py-600">
+          <p className="font-designer-14r text-text-subtle">
+            문의 목록을 불러오지 못했어요. 잠시 후 다시 시도해 주세요.
+          </p>
+        </div>
+      ) : inquiries.length === 0 ? (

As per coding guidelines: "All errors must be handled via utils/error-handler.ts — never use alert(), always use useToastStore."

Also applies to: 69-73

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/`(service)/(my)/my-inquiry/page.tsx around lines 39 - 40, The page
currently treats a failed fetch as empty because it only reads `inquiries` from
useGetMyOneToOneInquiries(); update the component to detect and handle the query
error instead of rendering the “no inquiries” empty state: use the error
information returned by the hook (or catch the rejection) and pass it to the
centralized error handler in utils/error-handler.ts (which will use
useToastStore) rather than calling alert or silently rendering an empty list;
ensure both the initial query (useGetMyOneToOneInquiries) and the related code
at the other occurrence (lines ~69-73) call the same handler and render an error
UI or loading state until data is valid.

Comment on lines +95 to +96
const { data: detail, isLoading: detailLoading } =
useGetMyOneToOneInquiryDetail(expanded ? inquiry.oneToOneInquiryId : null);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

상세 조회 실패 시 패널이 비어 보입니다.

상세 쿼리가 실패하면 현재 분기에서는 아무 내용도 렌더링되지 않아서, 사용자가 빈 문의로 오해할 수 있습니다. 로딩/성공 외에 실패 상태도 명시적으로 처리해 주세요.

🔧 제안
-  const { data: detail, isLoading: detailLoading } =
+  const {
+    data: detail,
+    isLoading: detailLoading,
+    isError: isDetailError,
+  } =
     useGetMyOneToOneInquiryDetail(expanded ? inquiry.oneToOneInquiryId : null);
...
-          ) : detail ? (
+          ) : isDetailError ? (
+            <p className="font-designer-14r text-text-subtle">
+              문의 내용을 불러오지 못했어요. 잠시 후 다시 시도해 주세요.
+            </p>
+          ) : detail ? (

As per coding guidelines: "All errors must be handled via utils/error-handler.ts — never use alert(), always use useToastStore."

Also applies to: 139-173

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/`(service)/(my)/my-inquiry/page.tsx around lines 95 - 96, The detail
query call using useGetMyOneToOneInquiryDetail (with expanded ?
inquiry.oneToOneInquiryId : null) currently only handles loading/success and
leaves the panel empty on failure; update the component to read the hook's error
state (e.g., isError and error) alongside detail and detailLoading, call the
centralized error handler from utils/error-handler.ts (and/or useToastStore) to
report the error, and render an explicit error/fallback UI in the panel (a brief
error message or retry button) when isError is true so the user doesn’t see an
empty panel; apply the same pattern to the related rendering block around the
139-173 area.

Comment on lines +78 to +79
? 'border-b-2 border-rose-500 text-rose-500'
: 'font-designer-20r text-gray-800',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

하드코딩된 색상 클래스는 토큰 클래스로 교체해 주세요.

border-rose-500, text-rose-500, border-rose-300, hover:text-red-500는 디자인 토큰 규칙과 충돌합니다. global.css의 토큰 기반 색상 클래스로 통일하는 게 맞습니다.

As per coding guidelines src/app/**/*.{tsx,jsx}: Never use Tailwind base classes ... Always use project custom tokens from global.css.

Also applies to: 95-95, 235-235

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/`(service)/(my)/my-posts/page.tsx around lines 78 - 79, Replace
hardcoded Tailwind color classes in the conditional className (the ternary that
returns 'border-b-2 border-rose-500 text-rose-500' vs 'font-designer-20r
text-gray-800') and the other occurrences mentioned (the lines using
'border-rose-300' and 'hover:text-red-500') with the project’s design token
classes defined in global.css; locate the conditional that sets those class
strings in src/app/(service)/(my)/my-posts/page.tsx and swap each color class
(border-rose-500, text-rose-500, border-rose-300, hover:text-red-500,
text-gray-800 as appropriate) for the equivalent token-based classes (the token
names used elsewhere in global.css) so styling follows the token convention.

Comment thread src/app/(service)/(my)/my-posts/page.tsx Outdated
href={`/class/${feed.courseId}/lesson/${feed.lessonId}?feedId=${feed.feedId}`}
className="flex flex-col"
>
<div className="relative aspect-[3/2] w-full bg-gray-100">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

aspect-[3/2]는 금지된 arbitrary value입니다.

현재 경로(src/app) 규칙상 arbitrary 값을 사용할 수 없습니다. 프로젝트에 정의된 비율 토큰/유틸 클래스로 치환해 주세요.

As per coding guidelines src/**/*.{ts,tsx,css}: No Tailwind arbitrary values ... and src/app/**/*.{tsx,jsx}: Do not use arbitrary Tailwind values in className attributes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/`(service)/(my)/my-posts/page.tsx at line 248, Replace the forbidden
Tailwind arbitrary class "aspect-[3/2]" on the <div className="relative
aspect-[3/2] w-full bg-gray-100"> JSX element in page.tsx with the project’s
predefined aspect-ratio token/utility (e.g., the project's aspect token such as
aspect-<token> or ratio-<token>); locate that JSX node (in the default export
component of page.tsx) and swap the arbitrary value for the correct
token/utility class used across the codebase.

Comment on lines +26 to +29
inquiryContent: z
.string()
.min(1, '내용을 입력해 주세요.')
.max(2000, '내용은 2000자 이하로 입력해 주세요.'),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

공백만 있는 내용이 스키마를 통과합니다.

.min(1)만 쓰면 공백 문자열도 유효값이 됩니다. 지금 작성 페이지는 trim()으로 막고 있어서 검증 규칙이 이미 이 스키마와 어긋나 있습니다.

🔧 제안
  inquiryContent: z
    .string()
+   .trim()
    .min(1, '내용을 입력해 주세요.')
    .max(2000, '내용은 2000자 이하로 입력해 주세요.'),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/types/schemas/inquiry.schema.ts` around lines 26 - 29, The inquiryContent
string schema currently uses .min(1) which allows strings containing only
whitespace; update the schema for inquiryContent to reject whitespace-only input
by trimming or validating whitespace before length check—for example, apply
.transform(s => s.trim()) then .min(1, '내용을 입력해 주세요.') or use .refine(s =>
s.trim().length > 0, { message: '내용을 입력해 주세요.' }) so the schema behavior matches
the form's trim logic.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/(service)/(my)/my-posts/page.tsx (1)

410-417: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

next/image 원격 URL( feed.thumbnailUrl )은 렌더 전 remotePatterns 허용 호스트 검증이 필요합니다

  • src/app/(service)/(my)/my-posts/page.tsx에서 feed.thumbnailUrl && !imgError 조건만으로 <Image src={feed.thumbnailUrl} ... />를 렌더하고(410-417, 510-517), next.config.ts images.remotePatterns에 매칭되지 않는 URL이면 렌더 단계에서 예외가 날 수 있습니다. remotePatterns 기준(예: img1.kakaocdn.net, lh3.googleusercontent.com, test-api.zeroone.it.kr, api.zeroone.it.kr, www.zeroone.it.kr, **.r2.cloudflarestorage.com, 프로덕션 외 localhost 등)으로 URL의 hostname(및 필요 시 protocol/path)을 사전 가드한 뒤 허용되는 경우에만 <Image>를 렌더하세요(허용 불가 시 fallback div로 처리).
  • (동일 구간) className="relative aspect-[3/2] w-full bg-gray-100" 처럼 arbitrary 값/하드코딩 컬러가 섞여 있어 global.css 토큰으로 치환이 필요합니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/`(service)/(my)/my-posts/page.tsx around lines 410 - 417, The code
renders next/image with feed.thumbnailUrl when feed.thumbnailUrl && !imgError
which can throw at render if the remote host isn't allowed by
next.config.images.remotePatterns; before rendering the <Image> in the component
(the block using feed.thumbnailUrl, imgError, Image, and setImgError) validate
the URL's hostname (and protocol/path if needed) against the canonical
allowed-host list used in next.config (e.g., img1.kakaocdn.net,
lh3.googleusercontent.com, test-api.zeroone.it.kr, api.zeroone.it.kr,
www.zeroone.it.kr, *.r2.cloudflarestorage.com, localhost variants) and only
render <Image src={feed.thumbnailUrl} ... /> when it matches, otherwise render
the existing fallback div; also replace the hardcoded tailwind/bg class
"relative aspect-[3/2] w-full bg-gray-100" with a class or token referenced from
global.css (use the global CSS variable/token for the background color) so
styling uses the design tokens instead of arbitrary values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/payment/modals/class-refund-request-modal.tsx`:
- Around line 132-136: The onChange currently force-casts the dropdown value
with a bare assertion (onChange={(v) => setReason(v as
CourseRefundReasonCode)}); remove the bare as and perform a runtime guard
against valid REFUND_REASONS before calling setReason. Implement a small type
guard that checks whether the incoming v matches one of the known REFUND_REASONS
values (or has the expected discriminant/shape), then call setReason with the
narrowed value; if the guard fails, set a safe fallback (e.g., undefined or a
default reason). Update the SingleDropdown onChange to use this guard and
fallback instead of the bare cast, referencing SingleDropdown, REFUND_REASONS,
reason, setReason, and CourseRefundReasonCode.

In `@src/hooks/queries/course/course-api.ts`:
- Around line 974-987: The current useGetMyDraftBuilderFeeds hook always returns
an empty list because enabled: false and a hardcoded queryFn; fix by wiring it
to an actual readiness flag and/or real API: replace enabled: false with a
conditional flag (e.g., isBuilderFeedsAvailable or isBackendReady) and update
queryFn inside useGetMyDraftBuilderFeeds to call
axiosInstanceV5.get('members/me/builder-feeds/draft') and return data.content
when that flag is true, otherwise keep returning the placeholder ({ feeds: [],
totalCount: 0 }) but ensure the UI checks the same flag (or a returned status)
to show “준비 중” or hide the feature instead of silently showing an empty list;
reference useGetMyDraftBuilderFeeds, queryFn, queryKey and enabled when making
the change.
- Around line 310-329: The mutation useRequestCourseRefund currently only
handles success and leaves failures unhandled; add an onError handler to the
useMutation options that routes the caught error through the project's error
handler and toast store: call the shared error handler from
utils/error-handler.ts (or its exported function) and then use useToastStore to
show a user-facing message when mutationFn for paymentId /
CourseRefundCreateRequest fails, keeping existing onSuccess
(queryClient.invalidateQueries(['myCoursePayments'])) intact; reference the
useRequestCourseRefund function, mutationFn, onError, useToastStore, and
utils/error-handler.ts when making the change.

---

Outside diff comments:
In `@src/app/`(service)/(my)/my-posts/page.tsx:
- Around line 410-417: The code renders next/image with feed.thumbnailUrl when
feed.thumbnailUrl && !imgError which can throw at render if the remote host
isn't allowed by next.config.images.remotePatterns; before rendering the <Image>
in the component (the block using feed.thumbnailUrl, imgError, Image, and
setImgError) validate the URL's hostname (and protocol/path if needed) against
the canonical allowed-host list used in next.config (e.g., img1.kakaocdn.net,
lh3.googleusercontent.com, test-api.zeroone.it.kr, api.zeroone.it.kr,
www.zeroone.it.kr, *.r2.cloudflarestorage.com, localhost variants) and only
render <Image src={feed.thumbnailUrl} ... /> when it matches, otherwise render
the existing fallback div; also replace the hardcoded tailwind/bg class
"relative aspect-[3/2] w-full bg-gray-100" with a class or token referenced from
global.css (use the global CSS variable/token for the background color) so
styling uses the design tokens instead of arbitrary values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 414a6ef6-1797-4881-91ba-1fd95bfe3a1f

📥 Commits

Reviewing files that changed from the base of the PR and between b0b76f5 and e8031be.

📒 Files selected for processing (6)
  • src/app/(service)/(my)/class-payment-management/page.tsx
  • src/app/(service)/(my)/my-posts/page.tsx
  • src/components/payment/modals/class-cancel-payment-modal.tsx
  • src/components/payment/modals/class-refund-request-modal.tsx
  • src/hooks/queries/course/course-api.ts
  • src/types/api/course.types.ts

Comment on lines +132 to +136
<SingleDropdown
options={REFUND_REASONS}
value={reason}
onChange={(v) => setReason(v as CourseRefundReasonCode)}
placeholder="사유를 선택해 주세요."

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

드롭다운 값의 bare as 단언은 제거해 주세요.

onChange={(v) => setReason(v as CourseRefundReasonCode)}는 런타임 검증 없이 union 타입을 강제합니다. 가이드대로 in 가드 + fallback으로 좁혀서 상태를 설정해야 안전합니다.

As per coding guidelines: src/components/**/*.{ts,tsx}에서 “Use the in guard operator with fallback ... Never use bare as assertions without runtime guards”.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/payment/modals/class-refund-request-modal.tsx` around lines
132 - 136, The onChange currently force-casts the dropdown value with a bare
assertion (onChange={(v) => setReason(v as CourseRefundReasonCode)}); remove the
bare as and perform a runtime guard against valid REFUND_REASONS before calling
setReason. Implement a small type guard that checks whether the incoming v
matches one of the known REFUND_REASONS values (or has the expected
discriminant/shape), then call setReason with the narrowed value; if the guard
fails, set a safe fallback (e.g., undefined or a default reason). Update the
SingleDropdown onChange to use this guard and fallback instead of the bare cast,
referencing SingleDropdown, REFUND_REASONS, reason, setReason, and
CourseRefundReasonCode.

Comment on lines +310 to +329
export const useRequestCourseRefund = () => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: async ({
paymentId,
request,
}: {
paymentId: number;
request: CourseRefundCreateRequest;
}) => {
await axiosInstanceV5.post(
`course-payments/${paymentId}/refunds`,
request,
);
},
onSuccess: async () => {
await queryClient.invalidateQueries({ queryKey: ['myCoursePayments'] });
},
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

환불 요청 실패 시 사용자 피드백 경로가 비어 있습니다.

useRequestCourseRefund는 성공 시 무효화만 처리하고 실패 시 토스트/에러 처리(onError)가 없어 UX가 끊깁니다. 실패 케이스도 useToastStore(또는 공통 error-handler 경유)로 명시 처리해 주세요.

As per coding guidelines: src/**/*.{ts,tsx}에서 “All errors must be handled via utils/error-handler.ts ... use useToastStore”.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/hooks/queries/course/course-api.ts` around lines 310 - 329, The mutation
useRequestCourseRefund currently only handles success and leaves failures
unhandled; add an onError handler to the useMutation options that routes the
caught error through the project's error handler and toast store: call the
shared error handler from utils/error-handler.ts (or its exported function) and
then use useToastStore to show a user-facing message when mutationFn for
paymentId / CourseRefundCreateRequest fails, keeping existing onSuccess
(queryClient.invalidateQueries(['myCoursePayments'])) intact; reference the
useRequestCourseRefund function, mutationFn, onError, useToastStore, and
utils/error-handler.ts when making the change.

Comment thread src/hooks/queries/course/course-api.ts Outdated
Comment on lines +974 to +987
export const useGetMyDraftBuilderFeeds = () => {
return useQuery({
queryKey: ['myDraftBuilderFeeds'],
queryFn: async (): Promise<{
feeds: MyDraftBuilderFeedItemResponse[];
totalCount: number;
}> => {
// TODO: replace with real API call when backend is ready
// const { data } = await axiosInstanceV5.get('members/me/builder-feeds/draft');
// return data.content;
return { feeds: [], totalCount: 0 };
},
enabled: false,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

임시 저장 피드 쿼리가 영구 비활성이라 기능이 항상 빈 목록으로 보입니다.

현재 enabled: false + 고정 빈 배열 반환으로 실제 데이터 조회가 절대 일어나지 않습니다. 백엔드 미오픈 상태라면 UI에서 “준비 중”을 명시적으로 표시하거나, 기능 노출 자체를 조건부로 막는 편이 안전합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/hooks/queries/course/course-api.ts` around lines 974 - 987, The current
useGetMyDraftBuilderFeeds hook always returns an empty list because enabled:
false and a hardcoded queryFn; fix by wiring it to an actual readiness flag
and/or real API: replace enabled: false with a conditional flag (e.g.,
isBuilderFeedsAvailable or isBackendReady) and update queryFn inside
useGetMyDraftBuilderFeeds to call
axiosInstanceV5.get('members/me/builder-feeds/draft') and return data.content
when that flag is true, otherwise keep returning the placeholder ({ feeds: [],
totalCount: 0 }) but ensure the UI checks the same flag (or a returned status)
to show “준비 중” or hide the feature instead of silently showing an empty list;
reference useGetMyDraftBuilderFeeds, queryFn, queryKey and enabled when making
the change.

HA-SEUNG-JEONG and others added 3 commits May 25, 2026 08:29
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
useGetMyBuilderFeedManagement({ status: 'DRAFT' })로 통합

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- feed/write: draftFeedIdRef로 feedId 추적, 재저장 시 updateFeed 사용
- feed/write: 등록하기 시 기존 draft는 updateFeed(PUBLISHED)로 전환
- feed/write: edit mode 수정하기에 status: PUBLISHED 추가
- qa/write: 등록 성공 시 localStorage draft 정리 추가
- my-posts: useGetMyBuilderFeedManagement({ status: DRAFT })로 교체

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app/`(service)/(my)/my-posts/page.tsx:
- Around line 457-461: The editHref currently builds slug routes using
feed.courseId which can break slug-based routing; update the logic around
editHref (used where feed.courseId, feed.lessonId, feed.feedId are referenced)
to prefer feed.courseSlug when present and fall back safely (e.g., render no
link or a disabled/placeholder link) while the slug is not available, or ensure
the API returns courseSlug and use that to construct `/class/${courseSlug}` and
`/class/${courseSlug}/lesson/${feed.lessonId}?feedId=${feed.feedId}`; make sure
link rendering checks feed.courseSlug before outputting a slug route to avoid
broken links.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6621b314-ca1d-4d0e-a914-da134f67cbe0

📥 Commits

Reviewing files that changed from the base of the PR and between 29cafd3 and 0db37fc.

📒 Files selected for processing (6)
  • src/app/(class-lesson)/class/[slug]/lesson/[id]/_components/lesson-qna-submission-modal.tsx
  • src/app/(landing)/class/[slug]/(learning)/feed/write/page.tsx
  • src/app/(landing)/class/[slug]/(learning)/qa/write/page.tsx
  • src/app/(service)/(my)/my-posts/page.tsx
  • src/hooks/queries/course/course-api.ts
  • src/types/api/course.types.ts
💤 Files with no reviewable changes (1)
  • src/hooks/queries/course/course-api.ts
✅ Files skipped from review due to trivial changes (1)
  • src/app/(class-lesson)/class/[slug]/lesson/[id]/_components/lesson-qna-submission-modal.tsx

Comment on lines +457 to +461
// TODO: courseSlug not in API response — using courseId as slug placeholder
const editHref =
feed.lessonId !== null
? `/class/${feed.courseId}/lesson/${feed.lessonId}?feedId=${feed.feedId}`
: `/class/${feed.courseId}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

[slug] 라우트에 courseId를 대입하면 수정 링크가 깨질 수 있습니다.

현재 editHref"/class/${feed.courseId}"를 사용하고 있어, slug 기반 라우팅과 불일치할 가능성이 큽니다. courseSlug를 응답에 포함해 경로를 구성하거나, slug가 준비될 때까지 해당 분기 링크를 비활성/대체 처리해 주세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/`(service)/(my)/my-posts/page.tsx around lines 457 - 461, The
editHref currently builds slug routes using feed.courseId which can break
slug-based routing; update the logic around editHref (used where feed.courseId,
feed.lessonId, feed.feedId are referenced) to prefer feed.courseSlug when
present and fall back safely (e.g., render no link or a disabled/placeholder
link) while the slug is not available, or ensure the API returns courseSlug and
use that to construct `/class/${courseSlug}` and
`/class/${courseSlug}/lesson/${feed.lessonId}?feedId=${feed.feedId}`; make sure
link rendering checks feed.courseSlug before outputting a slug route to avoid
broken links.

임시저장 글 등록 시 피드 목록이 아닌 발행된 피드 상세로 이동.
신규 등록도 createFeed 응답의 feedId로 상세 페이지 이동.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
test.zeroone.it.kr DNS 불통 상태에서 전체 E2E 테스트가
net::ERR_NAME_NOT_RESOLVED로 실패하는 문제 해결.
커넥티비티 프리플라이트 추가 — 응답 없으면 STAGING_DOWN=true
설정 후 E2E 스텝 전체 스킵, 경고 메시지 출력.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
이전 bandaid fix(스테이징 다운 시 전체 스킵)를 대체.
- CI에서 Next.js 앱 직접 빌드 후 localhost:3000 서버 기동
- non-@auth 테스트: 항상 로컬 서버 대상 실행 (스테이징 불필요)
- @auth 테스트: 스테이징 가용 시에만 실행, 불가 시 경고 후 스킵

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@HA-SEUNG-JEONG HA-SEUNG-JEONG merged commit 69673b4 into develop May 25, 2026
11 checks passed
@HA-SEUNG-JEONG HA-SEUNG-JEONG deleted the fix/myPage branch May 25, 2026 00:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant